﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using TrainSimulator.Interfaces;
using TrainSimulator.State;
using TrainSimulator.Interfaces.DataGridView;

namespace TrainSimulator.Model.Train
{
    /// <summary>
    /// A full train set existing of train wagons, driver and engine
    /// </summary>
    public class Train : ITrain
    {
        public event TrainChangeHandler OnChange;
        public event ReverseHandler OnReverse;
        public event TrackHandler OnTrack;

        #region Event Class
        public class TrainEventArgs : ITrainEventArgs
        {
            public ITrain TrainSet { get; set; }
            public object OldValue { get; set; }
            public object NewValue { get; set; }

            public TrainEventArgs(ITrain ts, object oldv, object newv)
            {
                this.TrainSet = ts;
                this.OldValue = oldv;
                this.NewValue = newv;
            }
        }
        #endregion

        #region Properties

        public int ID { get; private set; }
        public string Description { get; set; }
        string IDgvTrain.CurrentState { get { return CurrentState.ToString(); } }
        public IState CurrentState { get; set; }
        public IState PreviousState { get; set; }


        public IWagon FirstWagon { get; private set; }
        public IWagon LastWagon { get; private set; }
        public string WagonsInfo { get { return "" + Wagons.Count; } }
        public List<IWagon> Wagons { get; private set; }

        private double _requestedSpeed;
        public double RequestedSpeed
        {
            get { return _requestedSpeed; }
            set
            {
                if (value <= this.MaxSpeed)
                {
                    double newValue = value;
                    double oldValue = _requestedSpeed;
                    if (!oldValue.Equals(newValue))
                    {
                        _requestedSpeed = newValue;
                        ITrainEventArgs args = new TrainEventArgs(this, oldValue, newValue);
                        TrainChangeHandler evt = OnChange;
                        if (evt != null)
                            evt(this, args); // Run event
                    }
                }
                else
                {
                    throw new Exception("Can not set speed higher than MaxSpeed (" + MaxSpeed + "m/s)");
                }
            }
        } // speed underneath max speed and ChangeHandler
        private double _actualSpeed;
        public double Speed
        {
            get { return _actualSpeed; }
            set
            {
                if (this.MaxSpeed != null && value <= this.MaxSpeed)
                {
                    double newValue = value;
                    double oldValue = _actualSpeed;
                    if (!oldValue.Equals(newValue))
                    {
                        _actualSpeed = newValue;
                        ITrainEventArgs args = new TrainEventArgs(this, oldValue, newValue);
                        TrainChangeHandler evt = OnChange;
                        if (evt != null)
                            evt(this, args);
                    }
                }
                else
                    throw new Exception("Can not set speed higher than MaxSpeed (" + MaxSpeed + "m/s)");
            }
        } // speed underneath max speed and ChangeHandler
        //public double Acceleration { get; set; }
        //public double Deacceleration { get; set; }
        private double _maxSpeed;
        public double MaxSpeed
        {
            get
            {
                if (Wagons.Count > 0)
                {
                    if (_maxSpeed < 0) // When new results is needed
                    {
                        _maxSpeed = Wagons[0].MaxSpeed;
                        for (int i = 1; i < Wagons.Count; i++)
                            if (_maxSpeed < Wagons[i].MaxSpeed)
                                _maxSpeed = Wagons[i].MaxSpeed;
                    }
                }
                //else
                //    throw new Exception("Max. speed undefined. No wagons in train");

                return _maxSpeed;
            }
            private set { _maxSpeed = value; }
        } // Max speed from the fastest wagon
        private double _length;
        public double Length
        {
            get
            {
                if (_length < 0) // When new results is needed
                {
                    _length = 0;
                    foreach (var w in Wagons)
                        _length += w.Length;
                }
                return _length;
            }
            private set { _length = value; }
        } // length of all wagons togeather

        private bool _reverse;
        public bool Reverse
        {
            get { return _reverse; }
            set
            {
                bool newValue = (Boolean)value;
                bool oldValue = _reverse;
                if (oldValue != newValue)
                {
                    _reverse = newValue;
                    ITrainEventArgs args = new TrainEventArgs(this, oldValue, newValue);

                    ReverseHandler reverseEvent = OnReverse;
                    if (reverseEvent != null)
                        reverseEvent(this, args);

                    TrainChangeHandler changeEvent = OnChange;
                    if (changeEvent != null)
                        changeEvent(this, args);
                }
            }
        } // add events ReverseHandler, ChangeHandler

        private ITrack _currentTrack;
        public ITrack CurrentTrack
        {
            get { return _currentTrack; }
            set
            {
                ITrack newValue = value;
                ITrack oldValue = _currentTrack;
                if (value != null && (oldValue != null && !oldValue.Equals(newValue)))
                {
                    PreviousTrack = _currentTrack;
                    _currentTrack = newValue;
                    ITrainEventArgs args = new TrainEventArgs(this, oldValue, newValue);

                    TrackHandler trackEvent = OnTrack;
                    if (trackEvent != null)
                        trackEvent(this, args);

                    TrainChangeHandler changeEvent = OnChange;
                    if (changeEvent != null)
                        changeEvent(this, args);
                }
                else // If current track is null
                {
                    PreviousTrack = _currentTrack;
                    _currentTrack = newValue;
                }
            }
        } // add events TrackHandler, ChangeHandler
        public ITrack PreviousTrack { get; set; }

        public ILocoDriver LocoDriver { get; set; }
        public ITrainEngine Engine { get; set; }

        #endregion // end of properties

        private Train()
        {
            this.Length = -1;

            this.FirstWagon = null;
            this.LastWagon = null;
            this.Wagons = new List<IWagon>();

            this.Speed = 0;
            
            this.MaxSpeed = -1;
        }
        public Train(int id, string description, ITrack currentTrack, bool reverse)
            : this()
        {
            this.ID = id;
            this.Description = description;
            this.CurrentTrack = currentTrack;
            this.Reverse = reverse;
            this.CurrentState = StateFactory.TrainStopped;
        }

        public void AddWagon(IWagon wagon)
        {
            this.Length = -1;
            this.MaxSpeed = -1;
            Wagons.Add(wagon);
        }

        public void SetState(IState state)
        {
            if (!CurrentState.Equals(state))
            {
                PreviousState = CurrentState;
                CurrentState = state;
            }
        }

        public void UpdateState()
        {
            CurrentState.Handle(this);
        }

        //public void StartTrain()
        //{
        //    CurrentState = StateFactory.TrainAccelerating("Accelerating", 30, this);
        //}

    }


}
